package br.edu.unifei.mestrado.commons.partition;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import br.edu.unifei.mestrado.commons.graph.GraphProperties;
import br.edu.unifei.mestrado.commons.graph.NodeWrapper;
import br.edu.unifei.mestrado.commons.graph.RelationshipWrapper;

public abstract class AbstractPartition {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	public static final int NO_PARTITION = -1;
	public static int NO_LEVEL = 0;

	public static int PART_1 = 1;
	public static int PART_2 = 2;
	public static int PART_N = PART_2;
	
	public static final int TWO_WAY = 2;

	private int level;
	private int k;

	private AbstractPartitionIndex index;

	public AbstractPartition(int level, int k, AbstractPartitionIndex index) {
		this.level = level;
		this.k = k;
		this.index = index;
	}

	/**
	 * Adiciona a aresta na lista de arestas do corte e recalcula o edgeCut.
	 * @param edge
	 * @return
	 */
	public boolean addEdgeToCut(RelationshipWrapper edge) {
		return index.addEdgeToCut(edge);
	}

	/**
	 * Remove a aresta da lista de arestas do corte e recalcula o edgeCut.
	 * @param edge
	 * @return
	 */
	public boolean removeEdgeFromCut(RelationshipWrapper edge) {
		return index.removeEdgeFromCut(edge);
	}

	/**
	 * Adiciona o node em seu setId correspondente e atualiza o corte.
	 * @param setId
	 * @param node
	 */
	public void addNodeToSet(int setId, NodeWrapper node) {
		index.insertToIndex(node, GraphProperties.PARTITION, setId);
	}

	/**
	 * Remove o node de seu setId correspondente e atualiza o corte.
	 * @param setId
	 * @param node
	 */
	public void removeNodeFromSet(int setId, NodeWrapper node) {
		index.removeFromIndex(node, GraphProperties.PARTITION, setId);
	}

	public Iterable<NodeWrapper> getNodesFrom(int setId) {
		return index.queryNodes(GraphProperties.PARTITION, setId);
	}
	
	public int getCutWeight() {
		logger.debug("Corte: cutWeight: {}", index.getCutWeightInternal());
		logger.trace("Arestas no corte: {}", new Object() {
			@Override
			public String toString() {
				StringBuffer b = new StringBuffer();
				int count = 0;
				for (RelationshipWrapper rel : index.queryRelationshipsOnCut()) {
					b.append(rel + ", ");
					count++;
				}
				return " qtd: " + count + " : " + b.toString();
			}
		});
		return index.getCutWeightInternal();
	}

	/**
	 * Monta um objeto BestPartition a partir do indice interno.
	 * @return
	 */
	public BestPartition createBestPartition() {
		BestPartition part = new BestPartition(level);

		for (int setId = 0; setId < k; setId++) {
			Iterable<NodeWrapper> nodes = getNodesFrom(setId);
			for (NodeWrapper node : nodes) {
				part.addNodeToSet(setId, node);
			}
		}

		for (RelationshipWrapper rel : index.queryRelationshipsOnCut()) {
			part.addEdgeToCut(rel);
		}
		return part;
	}

	public int getK() {
		return k;
	}

	public int getLevel() {
		return level;
	}

	public Iterable<RelationshipWrapper> getEdgeCut() {
		return index.queryRelationshipsOnCut();
	}
	
	public void printSets() {
		logger.info("Nodes from partition");
		for (int setId = 0; setId < k; setId++) {
			final Iterable<NodeWrapper> nodes = getNodesFrom(setId);

//			logger.info("set " + set + " (" + getSets().get(set).getSizeNodes() + "): ");
			logger.info("\tset " + setId + ": {}", new Object() {
				@Override
				public String toString() {
					StringBuffer b = new StringBuffer();
					for (NodeWrapper node : nodes) {
						b.append(node.getId() + ", ");
					}
					return b.toString();
				}
			});
		}
	}

}
